#pragma once

#include <Util.h>


template < class T >
class XPtr
{
public:
    XPtr()
    {
        m_ptr = NULL;
    }

    XPtr(T* ptr)
    {
        m_ptr = NULL;
        SetPtr(ptr, TRUE);
    }

    XPtr(const XPtr& ptr)
    {
        m_ptr = NULL;
        SetPtr(ptr.m_ptr, TRUE);
    }

    XPtr& operator = (T* ptr)
    {
        SetPtr(ptr, TRUE);
        return (*this);
    }

    XPtr& operator = (const XPtr& ptr)
    {
        SetPtr(ptr.m_ptr, TRUE);
        return (*this);
    }

    virtual ~XPtr()
    {
        SetPtr(NULL, FALSE);
    }

public:
    operator T*()
    {
        return m_ptr;
    }

    T** operator &()
    {
        XASSERT_WARNING(m_ptr == NULL, _T("m_ptr is no NULL!"));
        return &m_ptr;
    }

    T* operator ->()
    {
        return m_ptr;
    }

protected:
    void SetPtr(T* ptr, BOOL bAddRef)
    {
        if(m_ptr != ptr)
        {
            XSAFE_RELEASE(m_ptr);
            m_ptr = ptr;

            if(m_ptr != NULL && bAddRef)
                m_ptr->AddRef();
        }
    }

protected:
    T*  m_ptr;
};

template < class T >
class XQIPtr : public XPtr<T>
{
public:
    XQIPtr() : XPtr()
    {}

    XQIPtr(T* ptr) : XPtr(ptr)
    {}

    template < class D >
    XQIPtr(D* ptr)
    {
        m_ptr = NULL;
        SetQIPtr(ptr, XINTFID(T));
    }

    template <>
    XQIPtr(IUnknown* ptr)
    {
        m_ptr = NULL;
        SetQIPtr(ptr, __uuidof(IUnknown));
    }

    XQIPtr(const XQIPtr& ptr) : XPtr(ptr.m_ptr)
    {}

    template < class D >
    XQIPtr& operator = (D* ptr)
    {
        SetQIPtr(ptr, XINTFID(T));
        return (*this);
    }

    template <>
    XQIPtr& operator = (IUnknown* ptr)
    {
        SetQIPtr(ptr, __uuidof(T));
        return (*this);
    }

    XQIPtr& operator = (const XQIPtr& ptr)
    {
        SetPtr(ptr.m_ptr);
    }

    virtual ~XQIPtr()
    {
        SetPtr(NULL, FALSE);
    }

protected:
    template < class D >
    void SetQIPtr(D* ptr, const IID& iid)
    {
        if(ptr == NULL)
        {
            SetPtr(NULL, FALSE);
        }
        else
        {
            T* p = NULL;
            if(SUCCEEDED(ptr->QueryInterface(iid, (void**)&p)))
            {
                SetPtr(p, FALSE);
            }
            else
            {
                SetPtr(NULL, FALSE);
            }
        }
    }
};

template < class T >
class XInstPtr : public XPtr<T>
{
public:
    XInstPtr()
    {
        HRESULT hResult = Util::CreateObject(&m_ptr);
    }
};